/**
 * 
 */
package com.puneet.graphqueries;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import partitioner.PartitionDAO;

import com.puneet.graphqueries.MessageHandlerAction.Response;
import com.puneet.graphqueries.graphs.MapPartitionImpl;
import com.puneet.graphqueries.net.INetConstants;
import com.puneet.graphqueries.net.TCPMessageReceiver;
import com.puneet.graphqueries.net.TCPMessageSender;
import com.puneet.graphqueries.net.TCPStatusSendReceive;
import com.puneet.graphqueries.ssh.SSHExec;
import com.puneet.graphqueries.ssh.ScpTo;
import com.puneet.graphqueries.yamlconfig.Configuration;
import com.puneet.graphqueries.yamlconfig.Partition;
import com.puneet.graphqueries.yamlconfig.YamlUtil;

/**
 * The main class that starts up everything
 * @author puneet
 * 
 */
public class QueryRunner {
	private static Pattern statusCheckMessagePattern = Pattern.compile("^status:\\s*(.+?) source:\\s(.+?)$");

	public void run(Configuration config) throws IOException {
		File myInputFile = null;
		Partition localPartition = null;
		for (Partition p : config.getPartitions()) {
			// If partition is not local, copy the partition file over to the
			// server, and start the server
			if (!p.isLocal() && config.isInitServer()) {
				ScpTo scp = new ScpTo();
				if (!p.isSkipCopyFile()) {
					System.out.println("Sending File:" + p.getPartitionfile() + " to: " + p.getServer());
					scp.sendFile(p.getServer(), "puneet", p.getPartitionfile(), p.getPartitionfile());
				}
				// Also need to send it the modified YAML
				Configuration configCopy = YamlUtil.loadString(YamlUtil.convertConfigToString(config));
				configCopy.setInitServer(false);
				for (Partition pCopy : configCopy.getPartitions()) {
					System.out.println("P: " + p.getServer() + " PCOPY: " + pCopy.getServer());
					if (pCopy.getServer().equals(p.getServer())) {
						pCopy.setLocal(true);
					} else {
						pCopy.setLocal(false);
					}
				}
				// Send modified Yaml File
				YamlUtil.dumpYamlToFile(configCopy, "/tmp/tmp.yaml");
				scp.sendFile(p.getServer(), "puneet", "/tmp/tmp.yaml", "/tmp/config.yaml");

				// Now start the servers on remote machines with approporiate
				// arguments
				String runServerCommand = "nohup java -Xmx1500M -cp "
						+ System.getProperty("java.class.path")
						+ " com.puneet.graphqueries.QueryRunner /tmp/config.yaml > /home/puneet/gp/queryrunner.out 2>/home/puneet/gp/queryrunner.err < /dev/null &";
				SSHExec exec = new SSHExec();
				exec.executeCommand(p.getServer(), "puneet", runServerCommand);
			}
			if (p.isLocal()) {
				localPartition = p;
				myInputFile = new File(p.getPartitionfile());
				if (!myInputFile.exists()) {
					throw new FileNotFoundException("Input File does not exist");
				}
			}
		}
		// Now build the graph database from the ipfile.
		PartitionDAO graphDb = new MapPartitionImpl(myInputFile);
		
		// Start the message receiver
		TCPMessageReceiver messageReceiver = new TCPMessageReceiver(INetConstants.MESSAGE_SEND_RECEIVE_PORT);
		Thread messageReceiverThread = new Thread(messageReceiver);
		messageReceiverThread.start();
		PartitionMessageHandler partitionMessageHandler = null;
		if ("khop".equalsIgnoreCase(config.getQuery().getType())) {
			partitionMessageHandler = new KHopMessageHandler(Integer.parseInt(config.getQuery().getParam()), graphDb);
		}
		Thread statusThread = new TCPStatusSendReceive(partitionMessageHandler, messageReceiver, 2, config.isInitServer(),
				config.getPartitions());
		statusThread.start();
		Date start = new Date();
		String startNode = config.getQuery().getQueryNode();
		if (config.isInitServer()) {
			String msg = "node: " + startNode + " k:" + 0;
			partitionMessageHandler.handleMessage(msg, config, true);
		}
		while (true) {
			String msg = messageReceiver.takeFirstMessage();
			System.out.println("Took the message:" + msg);
			Matcher m = statusCheckMessagePattern.matcher(msg);
			if (m.matches()) {
				String query = m.group(1);
				String source = m.group(2);
				if ("done".equalsIgnoreCase(query)) {
					String sendmsg = "statusresponse: " + (partitionMessageHandler.done() && !messageReceiver.hasMessages())
							+ " source: " + localPartition.getServer();
					TCPMessageSender sender = new TCPMessageSender();
					sender.sendMessage(sendmsg, source);
				}
				continue;
			}
			MessageHandlerAction action = partitionMessageHandler.handleMessage(msg, config, false);
			System.out.println("Message Action:" + action.getTakeAction());
			if (action.getTakeAction() == Response.EXIT) {
				System.out.println("Exiting now");
				// statusThread.interrupt();
				// messageReceiverThread.interrupt();
				break;
			}

		}
		Date end = new Date();
		System.out.println(end);
		System.out.println(start);
		System.out.println("Runtime: " + ((end.getTime() - start.getTime()) / 1000.0));
		// If you are the init server wait for others to complete building their
		// respective databases. Then start
		// If you are not the init server, build database and then inform the
		// init server. After that wait on a Queue to have messages
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		if (args.length == 0) {
			System.err.println("Usage: $java " + "com.puneet.graphqueries.QueryRunner configFile");
			System.exit(-1);
		}
		File f = new File(args[0]);
		if (!f.exists()) {
			System.err.println("File:" + f.getName() + " does not exist. Please check the filepath");
			System.exit(-1);
		}

		Configuration config = YamlUtil.loadFile(args[0]);
		File opfile = new File(config.getOpconfig().getFile());
		if (opfile.exists()) {
			opfile.delete();
			opfile.createNewFile();
		}
		// for (String queryNode : config.getQuery().getNodes()) {
		
		QueryRunner qr = new QueryRunner();
		qr.run(config);
		System.out.println("IN main Exiting now");
		
		// }
		System.exit(0);
	}

}
